Skip to content

Add -help-json flag for machine-readable CLI help#7198

Open
ewels wants to merge 12 commits into
masterfrom
help-json-cli
Open

Add -help-json flag for machine-readable CLI help#7198
ewels wants to merge 12 commits into
masterfrom
help-json-cli

Conversation

@ewels

@ewels ewels commented Jun 2, 2026

Copy link
Copy Markdown
Member

Why

Coding agents and tooling increasingly drive the Nextflow CLI, but the only machine interface to it today is the rendered -help text. Scraping that output is brittle: it's formatted for humans, wraps to the terminal width, and changes layout without notice. An LLM (or any tool) wanting to know "what flags does nextflow run take, and what type is each one?" has to parse prose.

This adds a structured, stable contract for that question. It's a direct port of the idea in nf-core/tools#4310, which added --help-json to the nf-core CLI for the same reason — so agents can discover the CLI reliably one command at a time, rather than pulling the whole tree into context or guessing from help text.

The design follows the same principles as the nf-core PR:

  • Progressive disclosure. nextflow -help-json returns the global options plus a recursive index of the whole command tree by name + description (including nested sub-commands like module create) — the map of the CLI, without the detail. Drilling into a command (nextflow run -help-json) returns that command's full option/argument detail. Agents see the whole shape up front, then load detail only where they need it.
  • Introspection, not parsing. A small CliSchema class reads the JCommander @Parameter / @Parameters annotations directly — the same source of truth that drives -help — so the JSON can't drift from the real CLI.
  • Discoverable. A one-line tip on every -help screen (top-level and each sub-command) points tools and agents at the flag.

The flag is marked as a help-style option, so it works even when required arguments are missing (just like -help).

What it looks like

nextflow -help-json — global options + a recursive name/description index of the whole tree (no per-command detail; just the map):

Full nextflow -help-json output
{
  "name": "nextflow",
  "path": "nextflow",
  "usage": "nextflow [options] COMMAND [arg...]",
  "params": [
    {"name": "config", "kind": "option", "type": "List", "opts": ["-C"], "help": "Use the specified configuration file(s) overriding any defaults"},
    {"name": "jvmOpts", "kind": "option", "type": "Map", "opts": ["-D"], "help": "Set JVM properties"},
    {"name": "background", "kind": "option", "type": "boolean", "opts": ["-bg"], "help": "Execute nextflow in background", "is_flag": true},
    {"name": "userConfig", "kind": "option", "type": "List", "opts": ["-c", "-config"], "help": "Add the specified file to configuration set"},
    {
      "name": "ignoreConfigIncludes",
      "kind": "option",
      "type": "boolean",
      "opts": ["-config-ignore-includes"],
      "help": "Disable the parsing of config includes",
      "is_flag": true
    },
    {"name": "debug", "kind": "option", "type": "List", "opts": ["-debug"], "hidden": true},
    {"name": "logFile", "kind": "option", "type": "String", "opts": ["-log"], "help": "Set nextflow log file path"},
    {"name": "quiet", "kind": "option", "type": "boolean", "opts": ["-q", "-quiet"], "help": "Do not print information messages", "is_flag": true},
    {
      "name": "remoteDebug",
      "kind": "option",
      "type": "boolean",
      "opts": ["-remote-debug"],
      "help": "Enable JVM interactive remote debugging (experimental)",
      "is_flag": true
    },
    {
      "name": "selfUpdate",
      "kind": "option",
      "type": "boolean",
      "opts": ["-self-update"],
      "help": "Update nextflow to the latest version",
      "is_flag": true,
      "hidden": true
    },
    {"name": "syslog", "kind": "option", "type": "String", "opts": ["-syslog"], "help": "Send logs to syslog server (eg. localhost:514)"},
    {
      "name": "trace",
      "kind": "option",
      "type": "List",
      "opts": ["-trace"],
      "help": "Enable trace level logging for the specified package name - multiple packages can be provided separating them with a comma e.g. '-trace nextflow,io.seqera'"
    },
    {"name": "version", "kind": "option", "type": "boolean", "opts": ["-v", "-version"], "help": "Print the program version", "is_flag": true}
  ],
  "subcommands": {
    "auth": {
      "help": "Manage Seqera Platform authentication",
      "subcommands": {
        "config": {"help": "Configure Seqera Platform settings"},
        "login": {"help": "Authenticate with Seqera Platform"},
        "logout": {"help": "Remove authentication and revoke access token"},
        "status": {"help": "Show current authentication status and configuration"}
      }
    },
    "clean": {"help": "Clean up project cache and work directories"},
    "clone": {"help": "Clone a project into a folder"},
    "config": {"help": "Print a project configuration"},
    "console": {"help": "Launch Nextflow interactive console"},
    "drop": {"help": "Delete the local copy of a project"},
    "fs": {
      "help": "Perform filesystem operations",
      "subcommands": {
        "cat": {"help": "Print a file to the stdout"},
        "cp": {"help": "Copy a file"},
        "ls": {"help": "List the content of a folder"},
        "mv": {"help": "Move a file"},
        "rm": {"help": "Remove a file"},
        "stat": {"help": "Print file to meta info"}
      }
    },
    "help": {"help": "Print the usage help for a command"},
    "info": {"help": "Print project and system runtime information"},
    "inspect": {"help": "Inspect process settings in a pipeline project"},
    "kuberun": {"help": "Execute a workflow in a Kubernetes cluster (experimental)"},
    "launch": {"help": "Launch a workflow in Seqera Platform"},
    "lineage": {
      "help": "Explore workflows lineage metadata",
      "subcommands": {
        "check": {"help": "Checks the integrity of an lineage file path"},
        "diff": {"help": "Show differences between two lineage descriptions"},
        "find": {"help": "Find lineage metadata descriptions matching with a query"},
        "list": {"help": "List the executions with lineage enabled"},
        "render": {"help": "Render the lineage graph for a workflow output"},
        "view": {"help": "Print the description of a Lineage ID (lid)"}
      }
    },
    "lint": {"help": "Lint Nextflow scripts and config files"},
    "list": {"help": "List all downloaded projects"},
    "log": {"help": "Print executions log and runtime info"},
    "logfile": {"help": "Print the contents of a Nextflow log file"},
    "module": {
      "help": "Manage Nextflow modules",
      "subcommands": {
        "create": {"help": "Create a new module skeleton"},
        "install": {"help": "Install a module from the registry"},
        "list": {"help": "List all installed modules"},
        "publish": {"help": "Publish a module to the registry"},
        "remove": {"help": "Remove an installed module"},
        "run": {"help": "Run a module directly from the registry"},
        "search": {"help": "Search for modules in the registry"},
        "spec": {"help": "Generate a meta.yml spec for a local module"},
        "validate": {"help": "Validate a module structure and metadata"},
        "view": {"help": "Show module information and usage template"}
      }
    },
    "plugin": {
      "help": "Execute plugin-specific commands",
      "subcommands": {"create": {"help": "Create a new plugin project from the template"}, "install": {"help": "Install a plugin"}}
    },
    "pull": {"help": "Download or update a project"},
    "run": {"help": "Execute a pipeline project"},
    "secrets": {
      "help": "Manage pipeline secrets",
      "subcommands": {
        "delete": {"help": "Delete an entry from the secrets store"},
        "get": {"help": "Get a secret value with the name"},
        "list": {"help": "List all names in the secrets store"},
        "set": {"help": "Set a key-pair in the secrets store"}
      }
    },
    "self-update": {"help": "Update nextflow runtime to the latest available version"},
    "view": {"help": "View project script file(s)"}
  }
}

nextflow info -help-json — a single command's full detail (options first, arguments last; the hidden -dd is surfaced with a flag rather than dropped). Output is condensed: entries that fit within ~160 columns stay on one line (see args), and only longer ones expand:

{
  "name": "info",
  "path": "nextflow info",
  "usage": "nextflow info [options] [args...]",
  "params": [
    {"name": "detailed", "kind": "option", "type": "boolean", "opts": ["-d"], "help": "Show detailed information", "is_flag": true},
    {"name": "moreDetailed", "kind": "option", "type": "boolean", "opts": ["-dd"], "is_flag": true, "hidden": true},
    {"name": "format", "kind": "option", "type": "String", "opts": ["-o"], "help": "Output format, either: text (default), json, yaml"},
    {"name": "checkForUpdates", "kind": "option", "type": "boolean", "opts": ["-u", "-check-updates"], "help": "Check for remote updates", "is_flag": true},
    {"name": "args", "kind": "argument", "type": "List", "help": "project name"}
  ],
  "help": "Print project and system runtime information"
}

The flag is discoverable from the normal human-readable help. A one-line tip is appended to every -help screen — the top-level usage and each sub-command:

$ nextflow -help
Usage: nextflow [options] COMMAND [arg...]

Options:
  ...
Commands:
  ...
  view          View project script file(s)

Tip: add -help-json to any command for machine-readable help.
$ nextflow module -help
Usage: nextflow module <command> [options]

Commands:
  ...
  validate    Validate a module structure and metadata

Tip: add -help-json to any command for machine-readable help.

Notes

  • The universal meta flags (-h, -help, -help-json) are excluded from the reported parameters. Hidden options are included but flagged with "hidden": true (e.g. info's -dd), so tools can choose to surface or ignore them.
  • The root index recurses through the entire command tree (including nested sub-commands like module create, lineage view) but carries only names + help text — no params, aliases or paths. Full option/argument detail is disclosed by drilling into a command, e.g. nextflow module -help-json, which reports module's own options plus the full detail of each nested sub-command. plugin lists its statically-known sub-commands (install, create); its dynamic <plugin-name>:<command> calls are resolved at runtime and can't be introspected.
  • Output is rendered by a small width-aware formatter: it stays valid, human-readable JSON but condenses any object or array that fits within ~160 columns onto a single line, so a leaf node prints as {"help": "..."} rather than spread across several lines.
  • New unit tests in CliSchemaTest cover the root schema, a command schema, and alias surfacing. Existing LauncherTest tests still pass.

🤖 Generated with Claude Code

@netlify

netlify Bot commented Jun 2, 2026

Copy link
Copy Markdown

Deploy Preview for nextflow-docs-staging canceled.

Name Link
🔨 Latest commit 61155a9
🔍 Latest deploy log https://app.netlify.com/projects/nextflow-docs-staging/deploys/6a27b353d2f4d40007f081ce

Adds a global `-help-json` flag to the Nextflow CLI that prints the
current command's help, usage and options as JSON, plus an index of all
available commands at the root level. This lets tools and LLMs discover
the CLI without scraping the rendered `-help` text.

A new `CliSchema` class introspects the JCommander `@Parameter` /
`@Parameters` annotations to build the schema. A tip pointing at the
flag is added to the main usage screen.

Signed-off-by: Phil Ewels <phil.ewels@seqera.io>

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Phil Ewels <phil.ewels@seqera.io>
Signed-off-by: Phil Ewels <phil.ewels@seqera.io>

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Phil Ewels <phil.ewels@seqera.io>
@ewels ewels requested a review from a team as a code owner June 2, 2026 21:41

@christopher-hakkaart christopher-hakkaart left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor language suggestions.

Comment thread docs/reference/cli.md Outdated
Comment thread docs/cli.md Outdated
Comment thread docs/cli.md Outdated
Co-authored-by: Chris Hakkaart <chris.hakkaart@seqera.io>
Signed-off-by: Phil Ewels <phil.ewels@seqera.io>
ewels added a commit to ewels/rich-click that referenced this pull request Jun 8, 2026
Each subcommand entry now carries a one-line `help` (plus `aliases` and a
nested `subcommands` index where present) instead of being a bare names-only
tree, so an agent can see what each subcommand does and pick where to drill
without a round-trip. This also aligns the entry shape with the sibling
`-help-json` feature in Nextflow (nextflow-io/nextflow#7198), so a single
consumer can parse both.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ewels and others added 2 commits June 8, 2026 18:02
Previously hidden options were excluded from the machine-readable schema.
Keep them but flag each with `hidden: true`, matching the sibling rich-click
--help-json so a single consumer sees the same contract from both.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Phil Ewels <phil.ewels@seqera.io>
Commands that group sub-commands (auth, fs, lineage, module, secrets)
now describe them recursively under the `subcommands` key, so a single
-help-json call covers the whole command tree. JCommander-backed
sub-commands (module) get full option/argument detail; manually-parsed
ones surface a name + description index.

Adds a SubcommandAware interface that these commands implement, and
getDescription() on the auth/secrets sub-command interfaces so their
help text has a single source of truth.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Phil Ewels <phil.ewels@seqera.io>
@ewels

ewels commented Jun 8, 2026

Copy link
Copy Markdown
Member Author

New commits were to add recursive listing of all subcommands and their help (not their full usage, to keep context usage minimal). Just the help texts show, allowing the agent to jump straight to the nested subcommand it needs.

Should be done making changes now. [Edit: still messing around sorry] Ok done now.

ewels and others added 6 commits June 8, 2026 20:12
The tip pointing users at -help-json was only printed on the top-level
usage; every sub-command help screen omitted it despite the tip claiming
it applies to 'any command'. Centralise the message in a shared
Launcher.HELP_JSON_TIP constant and emit it from the leaf-command
(JCommander) path and each UsageAware container command. Also drop the
redundant 'as JSON' since the flag name already names the format.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Phil Ewels <phil.ewels@seqera.io>
The top-level -help-json now recurses through the whole command tree
(including nested sub-commands like 'module create'), but carries only
names + help text - the map of the CLI. Full option/argument detail is
still disclosed by drilling into a command, keeping progressive
disclosure intact.

Output is rendered by a small width-aware formatter instead of
JsonOutput.prettyPrint: it stays valid, human-readable JSON but condenses
any object or array that fits within ~100 columns onto a single line, so
a leaf node prints as {"help": "..."} rather than across many lines.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Phil Ewels <phil.ewels@seqera.io>
Raise the compact renderer's soft line-length budget from 100 to 160
columns, so each option entry typically prints on a single line.

Update docs/cli.md to describe the current behaviour: the top-level
index is recursive (the whole command tree by name + description), the
progressive-disclosure split between the lightweight root map and
full detail on drill-in, and that hidden options are included but
flagged with "hidden": true.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Phil Ewels <phil.ewels@seqera.io>
PR #7197 made CmdPlugin a UsageAware command with its own usage
rendering, which bypassed the -help-json tip that every other command
help screen shows. Restore it, and make CmdPlugin SubcommandAware so its
statically-known sub-commands (install, create) are surfaced in
-help-json - both at the root index and under 'nextflow plugin
-help-json' - matching the human help added in #7197. The dynamic
<plugin-name>:<command> form is resolved at runtime and stays out.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Phil Ewels <phil.ewels@seqera.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants